import pulp  # 导入PuLP库，用于线性规划建模
import pandas as pd  # 导入Pandas库，用于数据处理

# 读取附件1.xlsx文件中的耕地信息数据，并将其存储在DataFrame中，数据来自工作表“乡村的现有耕地”
file_path1 = '附件1.xlsx'
df_fields = pd.read_excel(file_path1, sheet_name='乡村的现有耕地')

# 读取附件2.xlsx文件中的作物统计数据，并将其存储在DataFrame中，数据来自工作表“2023年统计的相关数据”
file_path2 = '附件2.xlsx'
df_crops_data = pd.read_excel(file_path2, sheet_name='2023年统计的相关数据')

# 定义豆类作物的编号集合，表示特定编号的作物属于豆类，用于后续约束和逻辑处理
legume_crops = {1, 2, 3, 4, 5, 17, 18, 19}

# 创建一个字典，键为地块名称，值为该地块的面积（单位：亩），该字典用于后续计算
field_area = {row['地块名称']: row['地块面积/亩'] for _, row in df_fields.iterrows()}

# 获取适合种植单季作物的地块名称列表，地块类型为“平旱地”、“梯田”或“山坡地”的地块
single_season_fields = df_fields[df_fields['地块类型'].isin(['平旱地', '梯田', '山坡地'])]['地块名称'].tolist()

# 获取适合种植双季作物的地块名称列表，地块类型为“水浇地”、“普通大棚”或“智慧大棚”的地块
double_season_fields = df_fields[df_fields['地块类型'].isin(['水浇地', '普通大棚', '智慧大棚'])]['地块名称'].tolist()

# 获取地块类型为“水浇地”的地块名称，生成列表，用于后续表示适合种植双季作物的水浇地块
irrigated_fields = df_fields[df_fields['地块类型'] == '水浇地']['地块名称'].tolist()

# 获取地块类型为“普通大棚”的地块名称，生成列表，用于表示适合种植双季作物的普通大棚地块
normal_greenhouse_fields = df_fields[df_fields['地块类型'] == '普通大棚']['地块名称'].tolist()

# 获取地块类型为“智慧大棚”的地块名称，生成列表，用于表示适合种植双季作物的智慧大棚地块
smart_greenhouse_fields = df_fields[df_fields['地块类型'] == '智慧大棚']['地块名称'].tolist()

# 创建一个线性规划模型对象，命名为“Field_Planting_Optimization”
# 模型的目标是最大化种植收益，因此使用了 pulp.LpMaximize 来定义目标函数为“最大化”
optimization_model = pulp.LpProblem("Field_Planting_Optimization", pulp.LpMaximize)

# 定义单季作物的编号范围为1到15（适用于单季地块），并存储在列表 single_season_crops 中
single_season_crops = list(range(1, 16))  # 作物1-15，适用于单季地块

# 定义适合水浇地块种植的双季作物编号范围为16到37，并存储在列表 double_season_irrigated_crops 中
double_season_irrigated_crops = list(range(16, 38))  # 作物16-37，适用于水浇地

# 定义适合普通大棚种植的双季作物编号范围为23到41，并存储在列表 double_season_normal_greenhouse_crops 中
double_season_normal_greenhouse_crops = list(range(23, 42))  # 作物23-41，适用于普通大棚

# 定义适合智慧大棚种植的双季作物编号范围为27到34，并存储在列表 double_season_smart_greenhouse_crops 中
double_season_smart_greenhouse_crops = list(range(27, 35))  # 作物27-34，适用于智慧大棚

# 对作物数据的“销售单价/(元/斤)”列进行数据处理
# 如果该列的值是一个区间（如'10-12'），则取该区间的平均值作为销售单价
# 否则，如果是单一数值，则直接保留
df_crops_data['销售单价(元/斤)'] = df_crops_data['销售单价/(元/斤)'].apply(
    lambda x: (float(x.split('-')[0]) + float(x.split('-')[1])) / 2 if isinstance(x, str) else x
)


price_per_kg = {row['作物编号']: row['销售单价(元/斤)'] for _, row in df_crops_data.iterrows()}  # 销售单价字典
yield_per_acre = {row['作物编号']: row['亩产量/斤'] for _, row in df_crops_data.iterrows()}  # 亩产量字典
cost_per_acre = {row['作物编号']: row['种植成本/(元/亩)'] for _, row in df_crops_data.iterrows()}  # 种植成本字典

# 定义时间范围：从2024年到2030年，共7年。每一年都是一个整数值，表示不同年度的种植计划
years_range = list(range(2024, 2031))  # 从2024到2030

# 定义连续变量 x_var，用于表示每块地在每年种植特定作物的种植比例（范围为0到1）
# "x_var" 是一个多维变量字典，维度包括：
# 1. 地块（single_season_fields + double_season_fields）：包括单季和双季适用的地块
# 2. 作物（single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops）：包括单季和双季作物
# 3. 年份（years_range）：从2024年到2030年，每年一个变量
# 4. 季节（1, 2）：表示第一季或第二季
# 定义的范围是 0 到 1，表示种植比例，可以是部分种植或者完全种植
x_var = pulp.LpVariable.dicts("x_var",
                              (single_season_fields + double_season_fields,
                               single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops,
                               years_range, [1, 2]), 0, 1, cat='Continuous')

# 定义二进制变量 y_var1，用于辅助表示作物在某块地某年某季是否种植（取值为0或1）
# "y_var1" 是一个多维字典，维度与 x_var 相同：
# 1. 地块（single_season_fields + double_season_fields）：包括单季和双季适用的地块
# 2. 作物（single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops）：包括单季和双季作物
# 3. 年份（years_range）：从2024年到2030年
# 4. 季节（1, 2）：第一季或第二季
# 变量值为 0 或 1，表示是否种植
y_var1 = pulp.LpVariable.dicts("y_var1",
                               (single_season_fields + double_season_fields,
                                single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops,
                                years_range, [1, 2]), 0, 1, cat='Binary')

# 定义另一个二进制变量 y_var2，也用于辅助表示作物在某块地某年某季是否种植（取值为0或1）
# "y_var2" 的维度与 "x_var" 和 "y_var1" 相同，分别为：
# 1. 地块（single_season_fields + double_season_fields）
# 2. 作物（single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops）
# 3. 年份（years_range）
# 4. 季节（1, 2）
# 变量的值为0或1，表示作物在某块地是否种植
y_var2 = pulp.LpVariable.dicts("y_var2",
                               (single_season_fields + double_season_fields,
                                single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops,
                                years_range, [1, 2]), 0, 1, cat='Binary')

# 目标函数：最大化所有单季地块的净收益
# 对于每个单季地块的每一年，计算净收益 = (销售单价 * 亩产量 - 种植成本) * 地块面积 * 种植比例
# 对所有单季地块、作物和年份的净收益进行加总，作为单季作物的目标函数
Z_single_season = pulp.lpSum(
    (price_per_kg[crop] * yield_per_acre[crop] * field_area[field]  # 销售收入部分：销售单价 * 亩产量 * 地块面积
    - cost_per_acre[crop] * field_area[field])  # 减去种植成本部分：种植成本 * 地块面积
    * x_var[field][crop][year][1]  # 乘以种植比例变量，表示地块在特定年份种植的作物比例
    for year in years_range  # 遍历所有年份
    for field in single_season_fields  # 遍历所有适合单季作物的地块
    for crop in single_season_crops  # 遍历所有单季作物
)

# 双季地块目标函数：最大化水浇地的净收益
# 对于水浇地，每一年有两季作物，净收益 = (销售单价 * 亩产量 - 种植成本) * 地块面积 * 第一季和第二季种植比例
Z_irrigated = pulp.lpSum(
    (price_per_kg[crop] * yield_per_acre[crop] * field_area[field]  # 销售收入部分：销售单价 * 亩产量 * 地块面积
    - cost_per_acre[crop] * field_area[field])  # 减去种植成本部分：种植成本 * 地块面积
    * (x_var[field][crop][year][1] + x_var[field][crop][year][2])  # 第一季和第二季作物的种植比例相加
    for year in years_range  # 遍历所有年份
    for field in irrigated_fields  # 遍历所有水浇地地块
    for crop in double_season_irrigated_crops  # 遍历所有适合水浇地的双季作物
)

# 双季地块目标函数：最大化普通大棚的净收益
# 与水浇地类似，普通大棚也种植两季作物，计算方式相同，适用于普通大棚的作物
Z_normal_greenhouse = pulp.lpSum(
    (price_per_kg[crop] * yield_per_acre[crop] * field_area[field]  # 销售收入部分
    - cost_per_acre[crop] * field_area[field])  # 减去种植成本部分
    * (x_var[field][crop][year][1] + x_var[field][crop][year][2])  # 第一季和第二季种植比例
    for year in years_range  # 遍历所有年份
    for field in normal_greenhouse_fields  # 遍历所有普通大棚地块
    for crop in double_season_normal_greenhouse_crops  # 遍历适合普通大棚的作物
)

# 双季地块目标函数：最大化智慧大棚的净收益
# 与普通大棚类似，计算智慧大棚的净收益
Z_smart_greenhouse = pulp.lpSum(
    (price_per_kg[crop] * yield_per_acre[crop] * field_area[field]  # 销售收入部分
    - cost_per_acre[crop] * field_area[field])  # 减去种植成本部分
    * (x_var[field][crop][year][1] + x_var[field][crop][year][2])  # 第一季和第二季种植比例
    for year in years_range  # 遍历所有年份
    for field in smart_greenhouse_fields  # 遍历所有智慧大棚地块
    for crop in double_season_smart_greenhouse_crops  # 遍历适合智慧大棚的作物
)

# 将四个目标函数（单季地块、水浇地、普通大棚、智慧大棚的净收益）相加，作为总的优化目标
# 优化模型的目标是最大化这四类地块的综合净收益
optimization_model += Z_single_season + Z_irrigated + Z_normal_greenhouse + Z_smart_greenhouse

# 为每个地块、作物、年份和季节设置约束条件，确保变量 x_var、y_var1 和 y_var2 之间的一致性
# x_var 表示作物种植的比例，y_var1 和 y_var2 是辅助二进制变量，确保作物种植的决策是合理的
for field in single_season_fields + irrigated_fields + normal_greenhouse_fields + smart_greenhouse_fields:
    for crop in single_season_crops + double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops:
        for year in years_range:
            for season in [1, 2]:
                # 约束条件：x_var 的值由 y_var1 和 y_var2 决定，y_var1 为 0.5，y_var2 为 1
                # 这意味着在某些情况下，可以部分种植作物（x_var = 0.5）或完全种植（x_var = 1）
                optimization_model += x_var[field][crop][year][season] == 0.5 * y_var1[field][crop][year][season] + y_var2[field][crop][year][season]
                # 约束条件：y_var1 和 y_var2 不能同时为 1，即某地块某季节的种植比例不会超过 1
                optimization_model += y_var1[field][crop][year][season] + y_var2[field][crop][year][season] <= 1

# 设置每个年份的地块种植约束，确保种植比例的总和为 1，即每个地块每季只能完全用于种植某作物
for year in years_range:
    # 对单季地块设置约束，确保每块单季地块在每年只能种植一种单季作物，且总比例为 1
    for field in single_season_fields:
        optimization_model += pulp.lpSum(x_var[field][crop][year][1] for crop in single_season_crops) == 1
    # 对水浇地设置约束，第一季的种植比例为 1，第二季的种植比例也为 1
    for field in irrigated_fields:
        optimization_model += pulp.lpSum(x_var[field][crop][year][1] for crop in range(16, 35)) == 1
        optimization_model += pulp.lpSum(x_var[field][crop][year][2] for crop in [16, 35, 36, 37]) == 1
    # 对普通大棚设置约束，第一季和第二季的种植比例分别为 1
    for field in normal_greenhouse_fields:
        optimization_model += pulp.lpSum(x_var[field][crop][year][1] for crop in range(17, 35)) == 1
        optimization_model += pulp.lpSum(x_var[field][crop][year][2] for crop in range(38, 42)) == 1
    # 对智慧大棚设置约束，第一季和第二季的种植比例分别为 1
    for field in smart_greenhouse_fields:
        optimization_model += pulp.lpSum(x_var[field][crop][year][1] for crop in range(17, 35)) == 1
        optimization_model += pulp.lpSum(x_var[field][crop][year][2] for crop in range(17, 35)) == 1

# 为双季地块设置跨年度的约束条件，确保在所有年份中，种植比例总和为 1
# 确保双季地块每季种植的作物比例总和为 1
for year in years_range:
    # 对单季地块设置约束，确保每年每块单季地块的种植比例总和为 1
    for field in single_season_fields:
        optimization_model += pulp.lpSum(x_var[field][crop][year][1] for crop in single_season_crops) == 1

    # 对双季地块（如水浇地、普通大棚、智慧大棚）设置约束，确保每年每块双季地块每季的种植比例为 1
    for field in double_season_fields:
        for season in [1, 2]:
            optimization_model += pulp.lpSum(x_var[field][crop][year][season] for crop in double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops) == 1

# 设置跨年度的约束条件，确保单季和双季地块不会连续种植同一作物
for year in years_range[:-1]:  # 不包括最后一年
    # 对单季地块，确保连续两年内同一块地不能连续种植同一作物
    for field in single_season_fields:
        for crop in single_season_crops:
            optimization_model += x_var[field][crop][year][1] + x_var[field][crop][year+1][1] <= 1

    # 对双季地块，确保在第一季和第二季的作物种植不会冲突
    for field in double_season_fields:
        for crop in double_season_irrigated_crops + double_season_normal_greenhouse_crops + double_season_smart_greenhouse_crops:
            optimization_model += x_var[field][crop][year][1] + x_var[field][crop][year][2] <= 1
            optimization_model += x_var[field][crop][year][2] + x_var[field][crop][year+1][1] <= 1

# 求解优化模型，得到最终的种植方案
optimization_model.solve()

